// Copyright (c) 2020 InCore Semiconductors Pvt. Ltd. see LICENSE.incore for more details on licensing terms
/*
Author: Arjun Menon, arjun@incoresemi.com
Created on: Friday 01 May 2020 05:19:02 PM IST

*/

module fpga_top#( parameter AXI_ID_WIDTH = 1, parameter AXI_ADDR_WIDTH = 28,
                  parameter GPIO_NUM = 22) (
  // ---- DDR ports --------------//
  inout  [15:0] ddr3_dq,
  inout  [1:0]  ddr3_dqs_n,
  inout  [1:0]  ddr3_dqs_p,
  output [13:0] ddr3_addr,
  output [2:0]  ddr3_ba,
  output        ddr3_ras_n,
  output        ddr3_cas_n,
  output        ddr3_we_n,
  output        ddr3_reset_n,
  output        ddr3_ck_p,
  output        ddr3_ck_n,
  output        ddr3_cke,
  output        ddr3_cs_n,
  output [1:0]  ddr3_dm,
  output        ddr3_odt,
  output        init_calib_complete,

  // ---- JTAG ports ------- //
`ifndef BSCAN2E
  input       pin_tck,
  input       pin_trst,
  input       pin_tms,
  input       pin_tdi,
  output      pin_tdo,
`endif

  // ---- UART0 ports --------//
  input         uart0_SIN,
  output        uart0_SOUT,

  // ---- bootconfig ports --//
  input [1:0] boot_config,

  // ---- GPIO ports --------//
  inout [GPIO_NUM-1:0]  gpio,

  // ---- PWM ports --------//
  output [5:0]  pwmout,
  //output [5:0]  pwmoutbar,    //Uncomment if pwmoutbar is enabled

   // ---- SPI0 ports --------//
  //output        spi0_sclk,
  output        spi0_nss,
  output        spi0_mosi,
  input         spi0_miso,
  output        spi1_sclk,
  output [1:0]  spi1_nss,
  output        spi1_mosi,
  input         spi1_miso,
  output        spi2_sclk,
  output        spi2_nss,
  output        spi2_mosi,
  input         spi2_miso,

  // ---- QSPI ports -------//
  output        qspi0_clk_o,
  output        qspi0_ncs_o,
  inout  [3:0]  qspi0_io,

  // ---- System Reset ------//
  input         sys_rst,  //Active Low

  // ---- System Clock ------//
  input         sys_clk);

  // ---                       Register Instantiations                    --- //
  reg                               aresetn;

  // ---                       Wire Instantiations                       --- //
  wire                              soc_reset;      // reset to the SoC
  wire                              core_clk;       // clock to the SoC
  wire                              ddr3_main;      // main clock to the ddr3-mig
  wire                              ddr3_ref;       // reference clock to dr3 mig
  wire                              locked;         // indicates pll is stable
  wire                              clk;            // mig ui clk
  wire                              rst;            // mig ui reset
  wire                              mmcm_locked;    // indicates the ui clock from mig is stable

  // Signals driven by axi converter to DDR slave ports
  wire [AXI_ID_WIDTH-1:0]           m_axi_awid;
  wire [AXI_ADDR_WIDTH-1:0]         m_axi_awaddr;
  wire [7:0]                        m_axi_awlen;
  wire [2:0]                        m_axi_awsize;
  wire [1:0]                        m_axi_awburst;
  wire [0:0]                        m_axi_awlock;
  wire [3:0]                        m_axi_awcache;
  wire [2:0]                        m_axi_awprot;
  wire                              m_axi_awvalid;
  wire                              m_axi_awready;
  wire [63:0]                       m_axi_wdata;
  wire [7:0]                        m_axi_wstrb;
  wire                              m_axi_wlast;
  wire                              m_axi_wvalid;
  wire                              m_axi_wready;
  wire                              m_axi_bready;
  wire [AXI_ID_WIDTH-1:0]           m_axi_bid;
  wire [1:0]                        m_axi_bresp;
  wire                              m_axi_bvalid;
  wire [AXI_ID_WIDTH-1:0]           m_axi_arid;
  wire [AXI_ADDR_WIDTH-1:0]         m_axi_araddr;
  wire [7:0]                        m_axi_arlen;
  wire [2:0]                        m_axi_arsize;
  wire [1:0]                        m_axi_arburst;
  wire [0:0]                        m_axi_arlock;
  wire [3:0]                        m_axi_arcache;
  wire [2:0]                        m_axi_arprot;
  wire                              m_axi_arvalid;
  wire                              m_axi_arready;
  wire                              m_axi_rready;
  wire [AXI_ID_WIDTH-1:0]           m_axi_rid;
  wire [63:0]                       m_axi_rdata;
  wire [1:0]                        m_axi_rresp;
  wire                              m_axi_rlast;
  wire                              m_axi_rvalid;
  // Signals from SoC Master to axi converter
  wire [AXI_ID_WIDTH-1:0]           s_axi_awid;
  wire [AXI_ADDR_WIDTH-1:0]         s_axi_awaddr;
  wire [7:0]                        s_axi_awlen;
  wire [2:0]                        s_axi_awsize;
  wire [1:0]                        s_axi_awburst;
  wire [0:0]                        s_axi_awlock;
  wire [3:0]                        s_axi_awcache;
  wire [2:0]                        s_axi_awprot;
  wire                              s_axi_awvalid;
  wire                              s_axi_awready;
  wire [63:0]                       s_axi_wdata;
  wire [7:0]                        s_axi_wstrb;
  wire                              s_axi_wlast;
  wire                              s_axi_wvalid;
  wire                              s_axi_wready;
  wire                              s_axi_bready;
  wire [AXI_ID_WIDTH-1:0]           s_axi_bid;
  wire [1:0]                        s_axi_bresp;
  wire                              s_axi_bvalid;
  wire [AXI_ID_WIDTH-1:0]           s_axi_arid;
  wire [AXI_ADDR_WIDTH-1:0]         s_axi_araddr;
  wire [7:0]                        s_axi_arlen;
  wire [2:0]                        s_axi_arsize;
  wire [1:0]                        s_axi_arburst;
  wire [0:0]                        s_axi_arlock;
  wire [3:0]                        s_axi_arcache;
  wire [2:0]                        s_axi_arprot;
  wire                              s_axi_arvalid;
  wire                              s_axi_arready;
  wire                              s_axi_rready;
  wire [AXI_ID_WIDTH-1:0]           s_axi_rid;
  wire [63:0]                       s_axi_rdata;
  wire [1:0]                        s_axi_rresp;
  wire                              s_axi_rlast;
  wire                              s_axi_rvalid;
  wire [11:0]                       device_temp;
  wire [GPIO_NUM-1:0]               gpio_in, gpio_out, gpio_out_en;
  wire [3:0]                        qspi0_io_in, qspi0_io_out, qspi0_io_en;

  // ---------------------------------------------------------------------------- //
  `ifdef BSCAN2E
    wire                            wire_tck_clk;
    wire                            wire_trst;
    wire                            wire_capture;
    wire                            wire_run_test;
    wire                            wire_sel;
    wire                            wire_shift;
    wire                            wire_tdi;
    wire                            wire_tms;
    wire                            wire_update;
    wire                            wire_tdo;

    BSCANE2 #(
      .JTAG_CHAIN(4) // Value for USER command.
    )
    bse2_inst (
      .CAPTURE (wire_capture), // 1-bit output: CAPTURE output from TAP controller.
      .DRCK    (), // 1-bit output: Gated TCK output. When SEL is asserted, DRCK toggles when CAPTURE or SHIFT are asserted.
      .RESET   (wire_trst), // 1-bit output: Reset output for TAP controller.
      .RUNTEST (wire_run_test), // 1-bit output: Output asserted when TAP controller is in Run Test/Idle state.
      .SEL     (wire_sel), // 1-bit output: USER instruction active output.
      .SHIFT   (wire_shift), // 1-bit output: SHIFT output from TAP controller.
      .TCK     (wire_tck_clk), // 1-bit output: Test Clock output. Fabric connection to TAP Clock pin.
      .TDI     (wire_tdi), // 1-bit output: Test Data Input (TDI) output from TAP controller.
      .TMS     (wire_tms), // 1-bit output: Test Mode Select output. Fabric connection to TAP.
      .UPDATE  (wire_update), // 1-bit output: UPDATE output from TAP controller
      .TDO     (wire_tdo) // 1-bit input: Test Data Output (TDO) input for USER function.
    );
  `endif

  // --------- Address width truncation and Reset generation for SoC ------------ //
  wire [31:0] temp_s_axi_awaddr, temp_s_axi_araddr;
  assign s_axi_awaddr= temp_s_axi_awaddr [AXI_ADDR_WIDTH-1:0];
  assign s_axi_araddr= temp_s_axi_araddr [AXI_ADDR_WIDTH-1:0];

  proc_sys_reset_0 proc_reset_inst (
      .slowest_sync_clk (core_clk),
      .ext_reset_in     (sys_rst),
      .aux_reset_in     (init_calib_complete),
      .mb_debug_sys_rst (0),
      .dcm_locked       (locked),
      .mb_reset         (soc_reset),
      .bus_struct_reset (),
      .peripheral_reset (),
      .interconnect_aresetn (),
      .peripheral_aresetn ()
  );
  // ---------------------------------------------------------------------------- //
  // ---------- Clock divider ----------------//
  clk_divider clk_div (
    .clk_in1  (sys_clk),
    .clk_out1 (core_clk),
    .clk_out2 (ddr3_main),
    .clk_out3 (ddr3_ref),
    .resetn   (1'b1),
    .locked   (locked)
  );
  // ----------------------------------------- //
  // ------------ MIG for DDR3 ---------------//
  mig_ddr3 mig_ddr3 (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr),
    .ddr3_ba                        (ddr3_ba),
    .ddr3_cas_n                     (ddr3_cas_n),
    .ddr3_ck_n                      (ddr3_ck_n),
    .ddr3_ck_p                      (ddr3_ck_p),
    .ddr3_cke                       (ddr3_cke),
    .ddr3_ras_n                     (ddr3_ras_n),
    .ddr3_we_n                      (ddr3_we_n),
    .ddr3_dq                        (ddr3_dq),
    .ddr3_dqs_n                     (ddr3_dqs_n),
    .ddr3_dqs_p                     (ddr3_dqs_p),
    .ddr3_reset_n                   (ddr3_reset_n),
    .init_calib_complete            (init_calib_complete),

    .ddr3_cs_n                      (ddr3_cs_n),
    .ddr3_dm                        (ddr3_dm),
    .ddr3_odt                       (ddr3_odt),
    .ui_clk                         (clk),
    .ui_clk_sync_rst                (rst),
    .mmcm_locked                    (mmcm_locked),
    .aresetn                        (aresetn),
    .app_sr_req                     ( 1'b0),
    .app_ref_req                    ( 1'b0),
    .app_zq_req                     ( 1'b0),
    .app_sr_active                  (),
    .app_ref_ack                    (),
    .app_zq_ack                     (),
    // Slave Interface Write Address Ports
    .s_axi_awid                     (m_axi_awid),
    .s_axi_awaddr                   (m_axi_awaddr),
    .s_axi_awlen                    (m_axi_awlen),
    .s_axi_awsize                   (m_axi_awsize),
    .s_axi_awburst                  (m_axi_awburst),
    .s_axi_awlock                   (m_axi_awlock),
    .s_axi_awcache                  (m_axi_awcache),
    .s_axi_awprot                   (m_axi_awprot),
    .s_axi_awqos                    (4'h0),
    .s_axi_awvalid                  (m_axi_awvalid),
    .s_axi_awready                  (m_axi_awready),
    // Slave Interface Write Data Ports
    .s_axi_wdata                    (m_axi_wdata),
    .s_axi_wstrb                    (m_axi_wstrb),
    .s_axi_wlast                    (m_axi_wlast),
    .s_axi_wvalid                   (m_axi_wvalid),
    .s_axi_wready                   (m_axi_wready),
    // Slave Interface Write Response Ports
    .s_axi_bid                      (m_axi_bid),
    .s_axi_bresp                    (m_axi_bresp),
    .s_axi_bvalid                   (m_axi_bvalid),
    .s_axi_bready                   (m_axi_bready),
    // Slave Interface Read Address Ports
    .s_axi_arid                     (m_axi_arid),
    .s_axi_araddr                   (m_axi_araddr),
    .s_axi_arlen                    (m_axi_arlen),
    .s_axi_arsize                   (m_axi_arsize),
    .s_axi_arburst                  (m_axi_arburst),
    .s_axi_arlock                   (m_axi_arlock),
    .s_axi_arcache                  (m_axi_arcache),
    .s_axi_arprot                   (m_axi_arprot),
    .s_axi_arqos                    (4'h0),
    .s_axi_arvalid                  (m_axi_arvalid),
    .s_axi_arready                  (m_axi_arready),
    // Slave Interface Read Data Ports
    .s_axi_rid                      (m_axi_rid),
    .s_axi_rdata                    (m_axi_rdata),
    .s_axi_rresp                    (m_axi_rresp),
    .s_axi_rlast                    (m_axi_rlast),
    .s_axi_rvalid                   (m_axi_rvalid),
    .s_axi_rready                   (m_axi_rready),
    .sys_clk_i                      (ddr3_main),
    .clk_ref_i                      (ddr3_ref),
    .device_temp                    (device_temp),
    .sys_rst                        (locked)
  );

  always @(posedge clk) begin
    aresetn <= ~rst;
  end


  // Instantiating the clock converter between the SoC and DDR3 MIG
  clk_converter clock_converter (
    .s_axi_aclk(core_clk),
    .s_axi_aresetn(~soc_reset),
    .s_axi_awid(s_axi_awid),
    .s_axi_awaddr(s_axi_awaddr),
    .s_axi_awlen(s_axi_awlen),
    .s_axi_awsize(s_axi_awsize),
    .s_axi_awburst(s_axi_awburst),
    .s_axi_awlock(1'b0),
    .s_axi_awcache(4'b10),
    .s_axi_awprot(s_axi_awprot),
    .s_axi_awregion(4'b0),
    .s_axi_awqos(4'b0),
    .s_axi_awvalid(s_axi_awvalid),
    .s_axi_awready(s_axi_awready),
    .s_axi_wdata(s_axi_wdata),
    .s_axi_wstrb(s_axi_wstrb),
    .s_axi_wlast(s_axi_wlast),
    .s_axi_wvalid(s_axi_wvalid),
    .s_axi_wready(s_axi_wready),
    .s_axi_bid(s_axi_bid),
    .s_axi_bresp(s_axi_bresp),
    .s_axi_bvalid(s_axi_bvalid),
    .s_axi_bready(s_axi_bready),
    .s_axi_arid(s_axi_arid),
    .s_axi_araddr(s_axi_araddr),
    .s_axi_arlen(s_axi_arlen),
    .s_axi_arsize(s_axi_arsize),
    .s_axi_arburst(s_axi_arburst),
    .s_axi_arlock(1'b0),
    .s_axi_arcache(4'b10),
    .s_axi_arprot(s_axi_arprot),
    .s_axi_arregion(4'b0),
    .s_axi_arqos(4'b0),
    .s_axi_arvalid(s_axi_arvalid),
    .s_axi_arready(s_axi_arready),
    .s_axi_rid(s_axi_rid),
    .s_axi_rdata(s_axi_rdata),
    .s_axi_rresp(s_axi_rresp),
    .s_axi_rlast(s_axi_rlast),
    .s_axi_rvalid(s_axi_rvalid),
    .s_axi_rready(s_axi_rready),
    .m_axi_aclk(clk),
    .m_axi_aresetn(aresetn),
    .m_axi_awid(m_axi_awid),
    .m_axi_awaddr(m_axi_awaddr),
    .m_axi_awlen(m_axi_awlen),
    .m_axi_awsize(m_axi_awsize),
    .m_axi_awburst(m_axi_awburst),
    .m_axi_awlock(m_axi_awlock),
    .m_axi_awcache(m_axi_awcache),
    .m_axi_awprot(m_axi_awprot),
    .m_axi_awregion(),
    .m_axi_awqos(),
    .m_axi_awvalid(m_axi_awvalid),
    .m_axi_awready(m_axi_awready),
    .m_axi_wdata(m_axi_wdata),
    .m_axi_wstrb(m_axi_wstrb),
    .m_axi_wlast(m_axi_wlast),
    .m_axi_wvalid(m_axi_wvalid),
    .m_axi_wready(m_axi_wready),
    .m_axi_bid(m_axi_bid),
    .m_axi_bresp(m_axi_bresp),
    .m_axi_bvalid(m_axi_bvalid),
    .m_axi_bready(m_axi_bready),
    .m_axi_arid(m_axi_arid),
    .m_axi_araddr(m_axi_araddr),
    .m_axi_arlen(m_axi_arlen),
    .m_axi_arsize(m_axi_arsize),
    .m_axi_arburst(m_axi_arburst),
    .m_axi_arlock(m_axi_arlock),
    .m_axi_arcache(m_axi_arcache),
    .m_axi_arprot(m_axi_arprot),
    .m_axi_arregion(),
    .m_axi_arqos(),
    .m_axi_arvalid(m_axi_arvalid),
    .m_axi_arready(m_axi_arready),
    .m_axi_rid(m_axi_rid),
    .m_axi_rdata(m_axi_rdata),
    .m_axi_rresp(m_axi_rresp),
    .m_axi_rlast(m_axi_rlast),
    .m_axi_rvalid(m_axi_rvalid),
    .m_axi_rready(m_axi_rready)
  );


  // ---- Instantiating the ChromiteM SoC -------------//
  mkDebugSoc core(
    // Main Clock and Reset to the SoC
    .CLK                         (core_clk),
    .RST_N                       (~soc_reset),

    // JTAG port definitions
  `ifndef BSCAN2E
    .CLK_tck_clk               (pin_tck),
    .RST_N_trst                (pin_trst),
    .wire_tms_tms_in           (pin_tms),
    .wire_tdi_tdi_in           (pin_tdi),
    .wire_tdo                  (pin_tdo),
  `else
    .CLK_tck_clk               (wire_tck_clk),
    .RST_N_trst                (~wire_trst),
    .wire_capture_capture_in   (wire_capture),
    .wire_run_test_run_test_in (wire_run_test),
    .wire_sel_sel_in           (wire_sel),
    .wire_shift_shift_in       (wire_shift),
    .wire_tdi_tdi_in           (wire_tdi),
    .wire_tms_tms_in           (wire_tms),
    .wire_update_update_in     (wire_update),
    .wire_tdo                  (wire_tdo),
  `endif

    // UART0 port definitions
    .uart0_SIN                  (uart0_SIN),
    .uart0_SOUT                 (uart0_SOUT),

    .pwm_o                      (pwmout),
    //.pwm_comp                   (pwmoutbar),    //Uncomment if pwmoutbar is enabled

    // SPI0 port definitions
    .spi0_mosi                 (spi0_mosi),
    .spi0_sclk                 (spi0_sclk),
    .spi0_nss                  (spi0_nss),
    .spi0_miso_dat             (spi0_miso),
    // SPI1 port definitions
    .spi1_mosi                 (spi1_mosi),
    .spi1_sclk                 (spi1_sclk),
    .spi1_nss                  (spi1_nss),
    .spi1_miso_dat             (spi1_miso),
    // SPI2 port definitions
    .spi2_mosi                 (spi2_mosi),
    .spi2_sclk                 (spi2_sclk),
    .spi2_nss                  (spi2_nss),
    .spi2_miso_dat             (spi2_miso),

    .boot_config                (boot_config),

    // AXI4 Master interface to be connected to DDR3
    .ddr_AWVALID                (s_axi_awvalid),
    .ddr_AWID                   (s_axi_awid),
    .ddr_AWADDR                 (temp_s_axi_awaddr),
    .ddr_AWLEN                  (s_axi_awlen),
    .ddr_AWSIZE                 (s_axi_awsize),
    .ddr_AWBURST                (s_axi_awburst),
    .ddr_AWLOCK                 (),
    .ddr_AWCACHE                (),
    .ddr_AWPROT                 (s_axi_awprot),
    .ddr_AWQOS                  (),
    .ddr_AWREGION               (),
    .ddr_AWREADY                (s_axi_awready),

    .ddr_WVALID                 (s_axi_wvalid),
    .ddr_WDATA                  (s_axi_wdata),
    .ddr_WSTRB                  (s_axi_wstrb),
    .ddr_WLAST                  (s_axi_wlast),
    .ddr_WREADY                 (s_axi_wready),

    .ddr_BVALID                 (s_axi_bvalid),
    .ddr_BID                    (s_axi_bid),
    .ddr_BRESP                  (s_axi_bresp),
    .ddr_BREADY                 (s_axi_bready),

    .ddr_ARVALID                (s_axi_arvalid),
    .ddr_ARID                   (s_axi_arid),
    .ddr_ARADDR                 (temp_s_axi_araddr),
    .ddr_ARLEN                  (s_axi_arlen),
    .ddr_ARSIZE                 (s_axi_arsize),
    .ddr_ARBURST                (s_axi_arburst),
    .ddr_ARLOCK                 (),
    .ddr_ARCACHE                (),
    .ddr_ARPROT                 (s_axi_arprot),
    .ddr_ARQOS                  (),
    .ddr_ARREGION               (),
    .ddr_ARREADY                (s_axi_arready),

    .ddr_RVALID                 (s_axi_rvalid),
    .ddr_RID                    (s_axi_rid),
    .ddr_RDATA                  (s_axi_rdata),
    .ddr_RRESP                  (s_axi_rresp),
    .ddr_RLAST                  (s_axi_rlast),
    .ddr_RREADY                 (s_axi_rready),

    //Open AXI4 Master Interface
    .open_AWVALID               (),
    .open_AWID                  (),
    .open_AWADDR                (),
    .open_AWLEN                 (),
    .open_AWSIZE                (),
    .open_AWBURST               (),
    .open_AWLOCK                (),
    .open_AWCACHE               (),
    .open_AWPROT                (),
    .open_AWQOS                 (),
    .open_AWREGION              (),
    .open_AWREADY               (0),
    .open_WVALID                (),
    .open_WDATA                 (),
    .open_WSTRB                 (),
    .open_WLAST                 (),
    .open_WREADY                (0),
    .open_BVALID                (0),
    .open_BID                   (0),
    .open_BRESP                 (0),
    .open_BREADY                (),
    .open_ARVALID               (),
    .open_ARID                  (),
    .open_ARADDR                (),
    .open_ARLEN                 (),
    .open_ARSIZE                (),
    .open_ARBURST               (),
    .open_ARLOCK                (),
    .open_ARCACHE               (),
    .open_ARPROT                (),
    .open_ARQOS                 (),
    .open_ARREGION              (),
    .open_ARREADY               (0),
    .open_RVALID                (0),
    .open_RID                   (0),
    .open_RDATA                 (0),
    .open_RRESP                 (0),
    .open_RLAST                 (0),
    .open_RREADY                (),

    //GPIO ports
    .gpio_in_val                (gpio_in),
    .gpio_out_val               (gpio_out),
    .gpio_out_en                (gpio_out_en),
    
    //QSPI ports
    .qspi0_io_in                (qspi0_io_in),
    .qspi0_io_o               (qspi0_io_out),
    .qspi0_io_enable          (qspi0_io_en),
    .qspi0_clk_o              (qspi0_clk_o),
    .qspi0_ncs_o              (qspi0_ncs_o)
  );

  // ---- Creating Inout ports -------------//
  genvar index;
  generate
    for(index=0; index<GPIO_NUM; index= index+1)
    begin: connect_gpio_tristates
      IOBUF gpio_iobuf_inst (
        .O  (gpio_in[index]),
        .IO (gpio[index]),
        .I  (gpio_out[index]),
        .T  (~gpio_out_en[index])
      );
    end
  endgenerate


  generate
    for(index=0; index<4; index= index+1)
    begin: connect_qspi_tristates
      IOBUF qspi0_iobuf_inst (
        .O  (qspi0_io_in[index]),
        .IO (qspi0_io[index]),
        .I  (qspi0_io_out[index]),
        .T  (~qspi0_io_en[index])
      );
    end
  endgenerate

  // Enable if onboard flash or disable if external flash
  STARTUPE2#(.PROG_USR("False"),
               .SIM_CCLK_FREQ(0.0))   startupe2_inst1(
               .CFGCLK(open),
               .CFGMCLK(open),
               .EOS(open),
               .PREQ(open),
               .CLK(0),
               .GSR(0),
               .GTS(0),
               .KEYCLEARB(0),
               .PACK(0),
               .USRCCLKO(spi0_sclk),
               .USRCCLKTS(0),
               .USRDONEO(1),
               .USRDONETS(1));
endmodule

